/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
#ifndef GIMI_REPLYHANDLER_H
#define GIMI_REPLYHANDLER_H

#include <map>

#include "gimiutils.h"

#define GIMI_REPLYID_WASREPLIED               1
#define GIMI_REPLYID_WASNOTREPLIED            0
#define GIMI_REPLYID_TIMEOUTED                -1
#define GIMI_REPLYID_NOTFOUND                 -2

#define GIMI_REPLYSTATUS_NOTREPLIED           0
#define GIMI_REPLYSTATUS_REPLIED              1
#define GIMI_REPLYSTATUS_TIMEOUTED            -1

namespace gimi {

struct GIMIReply {
  public:
    GIMIReply();
    GIMIReply(gim::time tOut);
    ~GIMIReply();

    int status; ///< 0 for waiting for a reply, 1 if replied and -1 for timeouted.
    gim::time timeout; ///< Time when waiting for a reply should be ended.
    int datalength; ///< Length of contained data.
    char *data; ///< Data.

  private:
    // Prevents use of copy constructors
    GIMIReply(const GIMIReply&);
    const GIMIReply& operator=(const GIMIReply&);
};
  
// uint32_t is msguid, bool is replied-status.
typedef std::map< uint32_t, GIMIReply* > t_replyQueue;
typedef t_replyQueue::iterator t_replyQueueIterator;
typedef t_replyQueue::const_iterator t_replyQueueConstIterator;
typedef std::pair< uint32_t, GIMIReply* > t_replyPair;
  
/**
 * \class GIMIReplyHandler
 * Contains information on messages that are waiting
 * for a reply, and replies to them.
 * 
 */
class GIMIReplyHandler {

  public:
    GIMIReplyHandler();
    ~GIMIReplyHandler();

    /**
     * When ping is sent.
     * 
     * Adds given msguid as unreplied to queue.
     *
     * \return False if msgUid was already in queue. Else true.
     */
    bool addUnreplied(const uint32_t msgUid,
                      gim::time timeoutTime);

    /**
     * When timeouted.
     * 
     * Removes given msguid from unreplied queue.
     * Msguid is removed if found.
     *
     * \return GIMI_REPLYID_*
     */
    /*
    int timeoutUnreplied(const uint32_t msgUid,
                         char **data = NULL,
                         int *datalength = NULL);
    */
    
    /**
     * Go through list and mark messages timeouted if needed.
     * Notifies for update if some are marked timed out.
     * 
     * \return Number of messages marked timed out.
     */
    int doTimeouts();
    
    /**
     * When notified of update.
     * 
     * Checks if given msgUid was replied, if it was, it's datapointer is
     * copied and message removed from list.
     * When still waiting for a reply, nothing is done.
     * When timeouted, message is removed.
     *
     * \return GIMI_REPLYID_*
     */
    int checkForReply(const uint32_t msgUid,
                      char **data = NULL,
                      int *datalength = NULL);
    
    /**
     * When pingreply is received.
     * Notifies for update if status is changed.
     * 
     * msgUid is marked as replied.
     *
     * \return GIMI_REPLYID_NOTFOUND, GIMI_REPLYID_WASREPLIED if it was already
     * replied, GIMI_REPLYID_WASNOTREPLIED if all went ok.
     */
    int markPingReplied(const uint32_t msgUid);

    /**
     * When a reply containing data is received.
     * Notifies for update if status is changed.
     *
     * msgUid is marked as replied and datapointer is moved.
     * \return GIMI_REPLYID_NOTFOUND, GIMI_REPLYID_WASREPLIED if it was already
     * replied, GIMI_REPLYID_WASNOTREPLIED if all went ok.
     */
    int markDataReplied(const uint32_t msgUid,
                        const int datalength,
                        const char *data);

    /**
     * For debugging, print messages and their statuses.
     */
    void printReplies(int level = 10);

    ownCondHandle checkReplyCondHandle;
    
  private:
    // Prevents use of copy constructors
    GIMIReplyHandler(const GIMIReplyHandler&);
    const GIMIReplyHandler& operator=(const GIMIReplyHandler&);
    
    t_replyQueue replyQueue;

};

} // end namespace gimi

#endif // GIMI_REPLYHANDLER_H
